package com.livestocksolutions.util;

import java.awt.Component;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;

import javax.swing.JFormattedTextField;
import javax.swing.text.Document;

import net.sourceforge.jdatepicker.impl.DateComponentFormatter;
import net.sourceforge.jdatepicker.impl.JDatePanelImpl;
import net.sourceforge.jdatepicker.impl.JDatePickerImpl;

/**
 * Create a JDatePicker Object.
 * @author Adam Parr
 *
 * @.modified 02 February 2013
 */
public class JDatePickerDate extends JDatePickerImpl {
	private static final long serialVersionUID = 1L;
	/**
	 * The Date Formatter Object to Format a String into a Date or a Date into a String.
	 */
	protected static DateFormat sdf = DateFormat.getDateInstance(DateFormat.SHORT, Locale.getDefault());
	protected ArrayList<PropertyChangeListener> propertyChangeListener = new ArrayList<PropertyChangeListener>();
	/**
	 * Constructor.
	 * @param dateInstantPanel
	 * @param formatter The Formatter that will format the JTextFiled text when the value has been changed.
	 */
	public JDatePickerDate(JDatePanelImpl dateInstantPanel, JFormattedTextField.AbstractFormatter formatter) {
		super(dateInstantPanel,formatter);
		
		super.getJFormattedTextField().addPropertyChangeListener("value", new PropertyChangeListener(){
			@Override
			public void propertyChange(PropertyChangeEvent evt) {
				for(PropertyChangeListener p : propertyChangeListener){
					p.propertyChange(evt);
				}
			}
		});
	}

	/**
	 * Get the format of the Date expected.
	 * @return DateFormat that will convert a Date to or from a String.
	 */
	public static DateFormat getMediumDateFormat() {
		return sdf;
	}
	
	/**
	 * Create a JDatePicker with default attributes
	 * @return A JDatePickerDate Object.
	 */
	public static JDatePickerDate createJDatePicker() {
		return new JDatePickerDate(new JDatePanelImpl(null), null);
	}
	
	@Override
	protected JFormattedTextField.AbstractFormatter createDefaultFormatter() {
		return new DateFormatter();
	}
	
	/* (non-Javadoc)
	 * @see javax.swing.JComponent#setEnabled(boolean)
	 */
	@Override
	public void setEnabled(boolean enabled) {
		super.getJFormattedTextField().setToolTipText(enabled?"Enter a date in the format of: "+getDateFormat():null);
		setTextEditable(enabled);
		for(Component c : getComponents()){
			c.setEnabled(enabled);
			if(c==super.getJFormattedTextField()){
				c.setEnabled(true);
			}
		}
	}
	
	/**
	 * Set the value of the Date Field to the provided value.
	 * @param value The Object to set as the Date field value.
	 */
	public void setValue(Object value) {
		super.getJFormattedTextField().setValue(value);
	}
	
	/**
	 * Get the Value of the Date field as a String format.
	 * @return String value of the current Date set.
	 */
	public String getText() {
		return super.getJFormattedTextField().getText();
	}
	
	/**
	 * Get the current Date of this DatePicker.
	 * @return The Date represented in the JFormattedTextField or null if it is invalid.
	 */
	public Date getDate() {
		return parse(super.getJFormattedTextField().getText());
	}
	
	/**
	 * Set the Date field to the Given Date.
	 * @param date The date to set.
	 */
	public void setDate(Date date) {
		setDate(date!=null?sdf.format(date):"");
	}
	
	/**
	 * Set the Date field to the Given date in String format.
	 * @param dateString The String that represents the date to show
	 *        A <tt>""</tt> or <tt>null</tt> represents an empty field.
	 */
	public void setDate(String dateString) {
		super.getJFormattedTextField().setText(dateString);
	}
	
	/*(non-Javadoc)
	 * @see javax.swing.text.JTextComponent.getDocument()
	 */
	public Document getDocument(){
		return super.getJFormattedTextField().getDocument();
	}
	
	/**
	 * Convert a String into a Date.
	 * @param dateString The String that represents the Date.
	 * @return A Date representation of the String given, or Null if the string did not match.
	 */
	public static Date parse(String dateString){
		Date d = null;
		if(!dateString.trim().equals("")) {
			try {
				d =sdf.parse(dateString.replace("-","/").replace(".","/"));
			} catch (ParseException e) {
				e.printStackTrace();
			}
		}
		return d;
	}
	
	/**
	 * @deprecated Use {@link #getText()} or {@link #getDate()} instead
	 */
	@Deprecated
	@Override
	public JFormattedTextField getJFormattedTextField(){
		try {
			throw new Exception("Illegal access method");
		} catch (Exception e) {
			e.printStackTrace();
		}
		return super.getJFormattedTextField();
	}
	
	/**
	 * Convert a String into a Date.
	 * @param date The Date that represents the Date.
	 * @return A String representation of the Date given, or <tt>null</tt> if date was <tt>null</tt>.
	 */
	public static String format(Date date){
		return date!=null?sdf.format(date):"";
	}
	
	/**
	 * Check to see if the Date field is empty or not
	 * @return <tt>true</tt> if that field is empty or not. <tt>false</tt> otherwise
	 */
	public boolean isEmpty() {
		return getText().equals("");
	}
	
	/**
     * Get the String representation of the Date in the localised format.
     * @return String of the format of the Date.
     */
    public static String getDateFormat() {
    	final int DAY = 31;
    	final int MONTH = Calendar.DECEMBER;
    	final int YEAR = 2060;
    	final int YEAR2 = 60;
    	Calendar cal = Calendar.getInstance();
    	cal.set(Calendar.YEAR,YEAR);
    	cal.set(Calendar.MONTH, MONTH);
    	cal.set(Calendar.DATE, DAY);
    	
    	String s = format(cal.getTime());
    	String[] sa = s.split("/");
    	
    	String format = "";
    	
    	for(int x = 0; x<sa.length;x++) {
    		int iv = Integer.parseInt(sa[x].replaceAll("[^\\d.]", ""));
    		switch(iv) {
    		case DAY:
    			format+="dd/";
    			break;
    		case MONTH+1:
    		case MONTH:
    			format+="MM/";
    			break;
    		case YEAR:
    			format+="yyyy/";
    			break;
    		case YEAR2:
    			format+="yy/";
    			break;
    		}
    	}
    	
    	return format.substring(0,format.length()-1);
    }
    
    /**
     * Change the date format to a different value. Changing this will have an effect on all
     * Date-String and String-Date conversions that take place after the change occurs.
     * @param newFormat The pattern of what a date is expected to be in when shown as a String.
     */
    public static void setDateFormat(String newFormat){
    	if(newFormat!=null){
    		sdf = new SimpleDateFormat(newFormat);
    	}else{
    		sdf = DateFormat.getDateInstance(DateFormat.SHORT, Locale.getDefault());
    	}
    }
    
    /**
     * Get a long format of the date, involving appending the day as a String to the front,
     * and changing the month to the string representation, an example would be if the date
     * in the short form is "dd/MM/yyyy", then the long format would be "EEEE, dd MMMM, yyyy"
     * @return String representing the format a date should be formatted to in long format.
     * 
     * @see #getDateFormat()
     */
    public static String getDateFormatLong() {
    	return "EEEE, "+getDateFormat().replaceAll("MM", "MMMM").replaceAll("/y", ", y").replaceAll("/", " ");
    }
    
    @Override
    public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener){
    	if(propertyName.toLowerCase().equals("value")){
    		propertyChangeListener.add(listener);
    	}else{
    		super.addPropertyChangeListener(propertyName,listener);
    	}
    }
	
	/**
	 * Formats the JTextField to the expected date format.
	 * @author Adam Parr
	 */
	protected class DateFormatter extends DateComponentFormatter{
		private static final long serialVersionUID = 5997312768041129127L;
		/**
		 * The DateFormat that the date should be formatted to.
		 */
		DateFormat format;
		
		/**
		 * Constructor.
		 */
		public DateFormatter(){
			format = JDatePickerDate.getMediumDateFormat();
		}
		
		@Override
		public String valueToString(Object value) throws ParseException {
			Calendar cal = (Calendar)value;
			if (cal == null) {
				return "";
			}
			return format.format(cal.getTime());
		}
		
		@Override
		public Object stringToValue(String text) throws ParseException {
			if (text == null || text.equals("")) {
				return null;
			}
			Date date = format.parse(text.replaceAll("\\.", "/").replaceAll("-", "/").replaceAll(" ", "/").replaceAll(",", "/"));
			Calendar calendar = Calendar.getInstance();
			calendar.setTime(date);
			return calendar;
		}
	}
}
